#region Licence
// Copyright (c) 2008 Tasman Logic Ltd

// Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
// and associated documentation files (the "Software"), to deal in the Software without restriction, 
// including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 
// subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all copies 
// or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 
// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#endregion

using ActiveSharp.PropertyMapping;

using NUnit.Framework;

namespace ActiveSharp.Tests
{
    [TestFixture]
    public class FieldToPropertyMapTests
    {
        public abstract class AbstractTestClass
        {
            protected T GetValue<T>(ref T field)
            {
                return field;
            }

            protected void SetValue<T>(ref T field, T value)
            {
                field = value;
            }

            private string _foo2;

            public string Foo2
            {
                get { return _foo2; }
                set { SetValue(ref _foo2, value); }
            }
        }

        public abstract class AbstractTestClass2 : AbstractTestClass
        {
            private string _foo3;

            public string Foo3
            {
                get { return _foo3; }
                set { SetValue(ref _foo3, value); }
            }
        }

        public class TestClass : AbstractTestClass2
        {
            private string _foo;
            private int _bar;
            private decimal _baz;

            public string Foo
            {
                get { return _foo; }
                set { SetValue(ref _foo, value); }
            }

            public int Bar
            {
                get { return _bar; }
                set { SetValue(ref _bar, value + 5); // just adding something to value to make the MSIL harder to parse in our test
                }
            }

            public decimal Baz
            {
                get { return GetValue(ref _baz); }
            }
        }

        public class DerivedTestClass : TestClass
        {
            private string _derivedString;
            private int _derivedInt;

            public string DerivedString
            {
                get { return _derivedString; }
                set { SetValue(ref _derivedString, value); }
            }

            public int DerivedInt
            {
                get { return _derivedInt; }
                set { SetValue(ref _derivedInt, value); }
            }
        }

        [Test]
        public void CanMapFieldsToProperties()
        {
            FieldToPropertyMap m = new FieldToPropertyMap(typeof(TestClass));
            Assert.AreSame(typeof(TestClass).GetProperty("Foo"), m["_foo"]);
            Assert.AreSame(typeof(TestClass).GetProperty("Bar"), m["_bar"]);
            Assert.AreSame(typeof(TestClass).GetProperty("Baz"), m["_baz"]);
            Assert.AreSame(typeof(TestClass).GetProperty("Foo2"), m["_foo2"]);
            Assert.AreSame(typeof(TestClass).GetProperty("Foo3"), m["_foo3"]);
        }

        [Test]
        public void CanMapFieldsToPropertiesInDerivedClass()
        {
            FieldToPropertyMap m = new FieldToPropertyMap(typeof(DerivedTestClass));
            Assert.AreSame(typeof(DerivedTestClass).GetProperty("Foo"), m["_foo"]);
            Assert.AreSame(typeof(DerivedTestClass).GetProperty("Bar"), m["_bar"]);
            Assert.AreSame(typeof(DerivedTestClass).GetProperty("Baz"), m["_baz"]);
            Assert.AreSame(typeof(DerivedTestClass).GetProperty("DerivedString"), m["_derivedString"]);
            Assert.AreSame(typeof(DerivedTestClass).GetProperty("DerivedInt"), m["_derivedInt"]);
            Assert.AreSame(typeof(DerivedTestClass).GetProperty("Foo2"), m["_foo2"]);
            Assert.AreSame(typeof(DerivedTestClass).GetProperty("Foo3"), m["_foo3"]);
        }

        // todo: private properties in ancestor classes might cause problems
        // E.g. SetValue executes, knowing that "this" is of type Derived, but in response to setting of private property
        // defined in Base.  The FieldToPropertyMap for Derived will not have that field in it.
		// Solutions: walk the ancestry hierarchy to make sure private stuff gets stored.  Or, use separate classes for each level of hierarchy.

        //TODO better to key by FieldInfo, with corresponding change to OffsetToField map too. (Why? derived classes hiding ancestor field bby name)
        //watch out for lack of equality there, re reflected type in the Info objects.... (if we reflect on DeclaredOnly, in all cases, we should be OK...)

        //todo: write the rest of these tests
        [Test, Ignore]
        public void CanSetSameFieldMultipleTimesInSameProperty()
        {
        }

        [Test, Ignore]
        public void CannotSetTwoDifferentFieldsInSameProperty()
        {
        }
    }
}
